import {
	IExecuteFunctions,
	ILoadOptionsFunctions,
	INodeExecutionData,
	INodeType,
	INodeTypeDescription,
	INodeListSearchResult,
} from 'n8n-workflow';

import { ConcurrencyController } from './ConcurrencyController';

export class ParallelWorkflowExecutor implements INodeType {
	description: INodeTypeDescription = {
		displayName: '并行工作流执行器',
		name: 'parallelWorkflowExecutor',
		icon: 'fa:tasks',
		group: ['transform'],
		version: 1,
		subtitle: '={{$parameter["workflowSource"] === "fixed" ? "工作流: " + $parameter["workflowId"] : ($parameter["workflowSource"] === "input" ? "动态字段: " + $parameter["workflowIdField"] : "动态表达式") + " | 并发数: " + $parameter["concurrency"]}}',
		description: '并行执行工作流，支持可控的并发数量',
		defaults: {
			name: '并行工作流执行器',
			color: '#4A90E2',
		},
		inputs: ['main', 'workflow'],
		outputs: ['main'],
		inputNames: ['数据输入', '工作流定义'],
		properties: [
			{
				displayName: '工作流来源',
				name: 'workflowSource',
				type: 'options',
				options: [
					{
						name: '固定工作流',
						value: 'fixed',
						description: '使用固定的工作流ID或从列表选择',
					},
					{
						name: '从输入数据获取',
						value: 'input',
						description: '从每个输入项目中动态获取工作流ID',
					},
					{
						name: '从表达式获取',
						value: 'expression',
						description: '使用表达式动态计算工作流ID',
					},
					{
						name: '从连线获取工作流定义',
						value: 'workflow',
						description: '通过"工作流定义"输入端口接收工作流定义',
					},
				],
				default: 'fixed',
				description: '工作流ID的获取方式',
			},
			{
				displayName: '工作流',
				name: 'workflowId',
				type: 'resourceLocator',
				default: { mode: 'list', value: '' },
				required: true,
				description: '要并行执行的工作流',
				displayOptions: {
					show: {
						workflowSource: ['fixed'],
					},
				},
				modes: [
					{
						displayName: '从列表选择',
						name: 'list',
						type: 'list',
						placeholder: '选择一个工作流...',
						typeOptions: {
							searchListMethod: 'getWorkflows',
							searchable: true,
						},
					},
					{
						displayName: '通过 ID',
						name: 'id',
						type: 'string',
						placeholder: '例如: 1234',
						validation: [
							{
								type: 'regex',
								properties: {
									regex: '^[0-9]+$',
									errorMessage: '工作流 ID 必须是数字',
								},
							},
						],
					},
				],
			},
			{
				displayName: '工作流ID字段',
				name: 'workflowIdField',
				type: 'string',
				default: 'workflowId',
				required: true,
				placeholder: '例如: workflowId, workflow_id, id',
				description: '输入数据中包含工作流ID的字段名',
				displayOptions: {
					show: {
						workflowSource: ['input'],
					},
				},
			},
			{
				displayName: '工作流ID表达式',
				name: 'workflowIdExpression',
				type: 'string',
				default: '',
				required: true,
				placeholder: '例如: {{ $json.workflowId }}, {{ $item(0).$node["Previous Node"].json.id }}',
				description: '用于计算工作流ID的表达式',
				displayOptions: {
					show: {
						workflowSource: ['expression'],
					},
				},
			},
			{
				displayName: '工作流定义字段',
				name: 'workflowDefinitionField',
				type: 'string',
				default: 'workflow',
				required: true,
				placeholder: '例如: workflow, workflowDefinition, nodes',
				description: '工作流定义输入中包含工作流定义的字段名',
				displayOptions: {
					show: {
						workflowSource: ['workflow'],
					},
				},
			},
			{
				displayName: '工作流定义格式',
				name: 'workflowDefinitionFormat',
				type: 'options',
				options: [
					{
						name: 'n8n工作流JSON',
						value: 'n8n',
						description: '标准的n8n工作流定义格式',
					},
					{
						name: '节点数组',
						value: 'nodes',
						description: '仅包含节点定义的数组',
					},
					{
						name: '自定义格式',
						value: 'custom',
						description: '自定义的工作流定义格式',
					},
				],
				default: 'n8n',
				description: '工作流定义的数据格式',
				displayOptions: {
					show: {
						workflowSource: ['workflow'],
					},
				},
			},
			{
				displayName: '数据模式',
				name: 'dataMode',
				type: 'options',
				options: [
					{
						name: '每个输入项目单独执行',
						value: 'each',
						description: '为每个输入项目单独执行一次工作流',
					},
					{
						name: '所有输入项目一次执行',
						value: 'once',
						description: '将所有输入项目一次性传递给工作流执行',
					},
				],
				default: 'each',
				description: '如何将输入数据传递给工作流',
			},
			{
				displayName: '并发数',
				name: 'concurrency',
				type: 'number',
				default: 5,
				typeOptions: {
					minValue: 1,
					maxValue: 50,
				},
				description: '最大并行执行数量',
			},
			{
				displayName: '错误处理',
				name: 'onError',
				type: 'options',
				options: [
					{
						name: '继续执行',
						value: 'continue',
						description: '即使某些工作流失败，也继续执行其他工作流',
					},
					{
						name: '停止所有执行',
						value: 'stopAll',
						description: '如果任何工作流失败，停止所有执行',
					},
				],
				default: 'continue',
				description: '当工作流执行失败时的处理方式',
			},
			{
				displayName: '等待子工作流',
				name: 'waitForSubWorkflow',
				type: 'boolean',
				default: true,
				description: '是否等待子工作流完成执行后再继续',
			},
			{
				displayName: '保持顺序',
				name: 'preserveOrder',
				type: 'boolean',
				default: true,
				description: '是否保持结果的原始顺序',
			},
			{
				displayName: '包含执行元数据',
				name: 'includeMetadata',
				type: 'boolean',
				default: false,
				description: '是否在结果中包含执行元数据',
			},
			{
				displayName: '输出格式',
				name: 'outputFormat',
				type: 'options',
				options: [
					{
						name: '默认格式',
						value: 'default',
						description: '使用默认的输出格式（基于保持顺序设置）',
					},
					{
						name: '扁平化结果',
						value: 'flat',
						description: '将所有结果合并到一个数组中',
					},
					{
						name: '分组结果',
						value: 'grouped',
						description: '按工作流ID分组结果',
					},
					{
						name: '详细结果',
						value: 'detailed',
						description: '包含详细的执行信息和统计数据',
					},
					{
						name: '自定义结构',
						value: 'custom',
						description: '使用自定义的结果结构模板',
					},
				],
				default: 'default',
				description: '结果的输出格式和组织方式',
			},
			{
				displayName: '自定义结构模板',
				name: 'customTemplate',
				type: 'json',
				default: '{\n  "results": "{{results}}",\n  "summary": {\n    "total": "{{total}}",\n    "successful": "{{successful}}",\n    "failed": "{{failed}}"\n  }\n}',
				description: '自定义输出结构的JSON模板。可用变量：{{results}}, {{total}}, {{successful}}, {{failed}}, {{workflowId}}, {{executionTime}}',
				displayOptions: {
					show: {
						outputFormat: ['custom'],
					},
				},
			},
			{
				displayName: '分组字段',
				name: 'groupByField',
				type: 'string',
				default: 'workflowId',
				placeholder: '例如: workflowId, status, category',
				description: '用于分组结果的字段名（仅在分组结果格式下有效）',
				displayOptions: {
					show: {
						outputFormat: ['grouped'],
					},
				},
			},
		],
	};

	methods = {
		listSearch: {
			async getWorkflows(this: ILoadOptionsFunctions): Promise<INodeListSearchResult> {
				try {
					// Get the base URL for n8n instance
					const baseUrl = this.getRestApiUrl();

					// Make request to n8n's internal API to get workflows
					const response = await this.helpers.httpRequest({
						method: 'GET',
						url: `${baseUrl}/workflows`,
						json: true,
						headers: {
							'accept': 'application/json',
						},
					});

					// Transform the response to match INodeListSearchResult format
					const workflows = response.data || response;
					const results = workflows.map((workflow: any) => ({
						name: workflow.name || `工作流 ${workflow.id}`,
						value: workflow.id.toString(),
						url: workflow.id ? `${baseUrl.replace('/rest', '')}/workflow/${workflow.id}` : undefined,
					}));

					return {
						results,
					};
				} catch (error) {
					// If API call fails, return a helpful error message and fallback
					console.warn('无法获取工作流列表:', error);

					return {
						results: [
							{
								name: '⚠️ 无法加载工作流列表 - 请手动输入工作流ID',
								value: '',
							},
						],
					};
				}
			},
		},
	};

	async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
		const items = this.getInputData();
		const workflowSource = this.getNodeParameter('workflowSource', 0) as string;
		const dataMode = this.getNodeParameter('dataMode', 0) as string;
		const concurrency = this.getNodeParameter('concurrency', 0) as number;
		const onError = this.getNodeParameter('onError', 0) as string;
		const waitForSubWorkflow = this.getNodeParameter('waitForSubWorkflow', 0) as boolean;
		const preserveOrder = this.getNodeParameter('preserveOrder', 0) as boolean;
		const includeMetadata = this.getNodeParameter('includeMetadata', 0) as boolean;
		const outputFormat = this.getNodeParameter('outputFormat', 0) as string;
		const customTemplate = this.getNodeParameter('customTemplate', 0, '{}') as string;
		const groupByField = this.getNodeParameter('groupByField', 0, 'workflowId') as string;

		// Get workflow definitions from the second input if using workflow source
		let workflowDefinitions: INodeExecutionData[] = [];
		if (workflowSource === 'workflow') {
			workflowDefinitions = this.getInputData(1) || [];
			if (workflowDefinitions.length === 0) {
				throw new Error('使用"从连线获取工作流定义"模式时，必须在"工作流定义"输入端口提供工作流定义数据');
			}
		}

		// Helper function to format output results
		const formatResults = (
			results: (INodeExecutionData[] | null)[],
			workflowIds: string[],
			executionStats: { total: number; successful: number; failed: number; executionTime: number }
		): INodeExecutionData[][] => {
			switch (outputFormat) {
				case 'flat': {
					// 扁平化所有结果
					const allResults: INodeExecutionData[] = [];
					for (const result of results) {
						if (result !== null) {
							allResults.push(...result);
						}
					}
					return [allResults];
				}

				case 'grouped': {
					// 按指定字段分组结果
					const groups: { [key: string]: INodeExecutionData[] } = {};

					for (let i = 0; i < results.length; i++) {
						const result = results[i];
						if (result !== null) {
							for (const item of result) {
								let groupKey: string;
								if (groupByField === 'workflowId') {
									groupKey = workflowIds[i] || 'unknown';
								} else {
									groupKey = String(item.json[groupByField] || 'unknown');
								}

								if (!groups[groupKey]) {
									groups[groupKey] = [];
								}
								groups[groupKey].push(item);
							}
						}
					}

					// 转换为输出格式
					const groupedResults: INodeExecutionData[] = [];
					for (const [groupKey, groupItems] of Object.entries(groups)) {
						groupedResults.push({
							json: {
								[groupByField]: groupKey,
								items: groupItems.map(item => item.json),
								count: groupItems.length,
							},
						});
					}
					return [groupedResults];
				}

				case 'detailed': {
					// 详细结果格式
					const allResults: INodeExecutionData[] = [];
					for (const result of results) {
						if (result !== null) {
							allResults.push(...result);
						}
					}

					const detailedResult: INodeExecutionData = {
						json: {
							results: allResults.map(item => item.json),
							summary: executionStats,
							metadata: {
								outputFormat: 'detailed',
								preserveOrder,
								includeMetadata,
								workflowIds: [...new Set(workflowIds)],
							},
						},
					};
					return [[detailedResult]];
				}

				case 'custom': {
					// 自定义模板格式
					try {
						const allResults: INodeExecutionData[] = [];
						for (const result of results) {
							if (result !== null) {
								allResults.push(...result);
							}
						}

						let template = customTemplate;

						// 替换模板变量
						template = template.replace(/\{\{results\}\}/g, JSON.stringify(allResults.map(item => item.json)));
						template = template.replace(/\{\{total\}\}/g, String(executionStats.total));
						template = template.replace(/\{\{successful\}\}/g, String(executionStats.successful));
						template = template.replace(/\{\{failed\}\}/g, String(executionStats.failed));
						template = template.replace(/\{\{executionTime\}\}/g, String(executionStats.executionTime));
						template = template.replace(/\{\{workflowId\}\}/g, JSON.stringify([...new Set(workflowIds)]));

						const customResult = JSON.parse(template);
						return [[{ json: customResult }]];
					} catch (error) {
						throw new Error(`自定义模板解析失败: ${error instanceof Error ? error.message : String(error)}`);
					}
				}

				default: {
					// 默认格式（基于preserveOrder设置）
					if (preserveOrder) {
						const finalResults: INodeExecutionData[][] = [];
						for (let i = 0; i < results.length; i++) {
							if (results[i] !== null) {
								finalResults.push(results[i]!);
							}
						}
						return finalResults;
					} else {
						// 扁平化结果
						const allResults: INodeExecutionData[] = [];
						for (const result of results) {
							if (result !== null) {
								allResults.push(...result);
							}
						}
						return [allResults];
					}
				}
			}
		};

		// Helper function to create temporary workflow from definition
		const createTemporaryWorkflow = async (workflowDefinition: any): Promise<string> => {
			try {
				const workflowDefinitionFormat = this.getNodeParameter('workflowDefinitionFormat', 0) as string;
				let workflowData: any;

				// Process workflow definition based on format
				switch (workflowDefinitionFormat) {
					case 'n8n':
						workflowData = workflowDefinition;
						break;
					case 'nodes':
						workflowData = {
							name: `临时工作流_${Date.now()}`,
							nodes: workflowDefinition,
							connections: {},
							active: false,
							settings: {},
							staticData: {}
						};
						break;
					case 'custom':
						// For custom format, assume it's already in the correct format
						workflowData = workflowDefinition;
						break;
					default:
						throw new Error(`不支持的工作流定义格式: ${workflowDefinitionFormat}`);
				}

				// Create temporary workflow via n8n API
				const baseUrl = this.getRestApiUrl();
				const response = await this.helpers.httpRequest({
					method: 'POST',
					url: `${baseUrl}/workflows`,
					json: true,
					body: workflowData,
					headers: {
						'accept': 'application/json',
						'content-type': 'application/json',
					},
				});

				return response.id.toString();
			} catch (error) {
				throw new Error(`创建临时工作流失败: ${error instanceof Error ? error.message : String(error)}`);
			}
		};

		// Helper function to get workflow ID for an item
		const getWorkflowIdForItem = (itemIndex: number): string => {
			switch (workflowSource) {
				case 'fixed': {
					const workflowId = this.getNodeParameter('workflowId', itemIndex) as { mode: string; value: string };
					return workflowId.value;
				}
				case 'input': {
					const workflowIdField = this.getNodeParameter('workflowIdField', itemIndex) as string;
					const item = items[itemIndex];
					const workflowId = item.json[workflowIdField];
					if (!workflowId) {
						throw new Error(`工作流ID字段 '${workflowIdField}' 在输入项目 ${itemIndex} 中未找到或为空`);
					}
					return String(workflowId);
				}
				case 'expression': {
					const workflowIdExpression = this.getNodeParameter('workflowIdExpression', itemIndex) as string;
					const workflowId = this.evaluateExpression(workflowIdExpression, itemIndex);
					if (!workflowId) {
						throw new Error(`工作流ID表达式在项目 ${itemIndex} 中计算结果为空`);
					}
					return String(workflowId);
				}
				case 'workflow': {
					// For workflow definitions, we'll handle this differently
					// Return a placeholder that will be replaced with actual workflow ID
					return `workflow_definition_${itemIndex}`;
				}
				default:
					throw new Error(`不支持的工作流来源: ${workflowSource}`);
			}
		};

		if (dataMode === 'each') {
			// Execute workflow for each input item with optimized concurrency control
			const results: (INodeExecutionData[] | null)[] = new Array(items.length).fill(null);
			const workflowIds: string[] = new Array(items.length);
			const startTime = Date.now();

			// For workflow definitions, create temporary workflows first
			const temporaryWorkflowIds: { [key: string]: string } = {};
			if (workflowSource === 'workflow') {
				const workflowDefinitionField = this.getNodeParameter('workflowDefinitionField', 0) as string;

				// Create unique temporary workflows for each unique definition
				const uniqueDefinitions = new Map<string, any>();
				for (let i = 0; i < items.length; i++) {
					const definitionIndex = Math.min(i, workflowDefinitions.length - 1);
					const workflowDef = workflowDefinitions[definitionIndex].json[workflowDefinitionField];
					if (workflowDef) {
						const defKey = JSON.stringify(workflowDef);
						if (!uniqueDefinitions.has(defKey)) {
							uniqueDefinitions.set(defKey, workflowDef);
						}
					}
				}

				// Create temporary workflows for each unique definition
				for (const [defKey, workflowDef] of uniqueDefinitions) {
					const tempWorkflowId = await createTemporaryWorkflow(workflowDef);
					temporaryWorkflowIds[defKey] = tempWorkflowId;
				}
			}

			// Create task functions for each item
			const tasks = items.map((_, itemIndex) => async (): Promise<INodeExecutionData[]> => {
				let currentWorkflowId: string;

				if (workflowSource === 'workflow') {
					const workflowDefinitionField = this.getNodeParameter('workflowDefinitionField', 0) as string;
					const definitionIndex = Math.min(itemIndex, workflowDefinitions.length - 1);
					const workflowDef = workflowDefinitions[definitionIndex].json[workflowDefinitionField];
					if (!workflowDef) {
						throw new Error(`工作流定义字段 '${workflowDefinitionField}' 在工作流定义项目 ${definitionIndex} 中未找到或为空`);
					}
					const defKey = JSON.stringify(workflowDef);
					currentWorkflowId = temporaryWorkflowIds[defKey];
				} else {
					currentWorkflowId = getWorkflowIdForItem(itemIndex);
				}

				workflowIds[itemIndex] = currentWorkflowId;

				if (waitForSubWorkflow) {
					const executionResult = await this.executeWorkflow(
						{ id: currentWorkflowId },
						[items[itemIndex]],
					);

					if (executionResult.data?.main) {
						const result = executionResult.data.main[0] || [];

						if (includeMetadata && result.length > 0) {
							result.forEach((item: INodeExecutionData) => {
								if (!item.metadata) item.metadata = {};
								(item.metadata as any).subExecution = {
									workflowId: currentWorkflowId,
									executionId: executionResult.executionId,
								};
							});
						}
						return result;
					} else {
						return [];
					}
				} else {
					const executionResult = await this.executeWorkflow(
						{ id: currentWorkflowId },
						[items[itemIndex]],
					);

					return [
						{
							...items[itemIndex],
							metadata: includeMetadata ? {
								subExecution: {
									workflowId: currentWorkflowId,
									executionId: executionResult.executionId,
								},
							} : undefined,
						},
					];
				}
			});

			// Execute tasks with optimized concurrency control
			const taskResults = await ConcurrencyController.execute(
				tasks,
				concurrency,
				onError === 'stopAll'
			);

			// Process results and handle errors
			for (let i = 0; i < taskResults.length; i++) {
				const result = taskResults[i];
				if (result instanceof Error) {
					if (onError === 'stopAll' && !this.continueOnFail()) {
						throw result;
					} else if (this.continueOnFail()) {
						results[i] = [
							{
								json: { error: result.message },
								pairedItem: { item: i },
							},
						];
					}
				} else {
					results[i] = result;
				}
			}



			// Calculate execution statistics
			const endTime = Date.now();
			const executionTime = endTime - startTime;
			const successful = results.filter(r => r !== null && !(r[0]?.json?.error)).length;
			const failed = results.filter(r => r !== null && r[0]?.json?.error).length;
			const total = items.length;

			const executionStats = {
				total,
				successful,
				failed,
				executionTime,
			};

			// Clean up temporary workflows if created
			if (workflowSource === 'workflow' && Object.keys(temporaryWorkflowIds).length > 0) {
				try {
					const baseUrl = this.getRestApiUrl();
					for (const tempWorkflowId of Object.values(temporaryWorkflowIds)) {
						await this.helpers.httpRequest({
							method: 'DELETE',
							url: `${baseUrl}/workflows/${tempWorkflowId}`,
							headers: {
								'accept': 'application/json',
							},
						});
					}
				} catch (error) {
					// Log warning but don't fail the execution
					console.warn('清理临时工作流时出现警告:', error);
				}
			}

			// Use the new formatting function
			return formatResults(results, workflowIds, executionStats);
		} else {
			// Execute workflow once with all items
			let temporaryWorkflowId: string | null = null;
			try {
				const startTime = Date.now();

				// For "once" mode, we need to determine which workflow to use
				// If using dynamic sources, we'll use the first item's workflow ID
				// or require all items to have the same workflow ID
				let targetWorkflowId: string;

				if (workflowSource === 'fixed') {
					targetWorkflowId = getWorkflowIdForItem(0);
				} else if (workflowSource === 'workflow') {
					// For workflow definitions in "once" mode, use the first definition
					const workflowDefinitionField = this.getNodeParameter('workflowDefinitionField', 0) as string;
					const workflowDef = workflowDefinitions[0].json[workflowDefinitionField];
					if (!workflowDef) {
						throw new Error(`工作流定义字段 '${workflowDefinitionField}' 在工作流定义项目 0 中未找到或为空`);
					}
					temporaryWorkflowId = await createTemporaryWorkflow(workflowDef);
					targetWorkflowId = temporaryWorkflowId;
				} else {
					// For dynamic sources in "once" mode, validate that all items have the same workflow ID
					const workflowIds = items.map((_, index) => getWorkflowIdForItem(index));
					const uniqueWorkflowIds = [...new Set(workflowIds)];

					if (uniqueWorkflowIds.length > 1) {
						throw new Error(`在"所有输入项目一次执行"模式下，所有项目必须使用相同的工作流ID。发现了 ${uniqueWorkflowIds.length} 个不同的工作流ID: ${uniqueWorkflowIds.join(', ')}`);
					}

					targetWorkflowId = uniqueWorkflowIds[0];
				}

				const executionResult = await this.executeWorkflow(
					{ id: targetWorkflowId },
					items,
				);

				const endTime = Date.now();
				const executionTime = endTime - startTime;

				if (!waitForSubWorkflow) {
					// For non-waiting mode, format the original items
					const results = [items];
					const workflowIds = [targetWorkflowId];
					const executionStats = {
						total: 1,
						successful: 1,
						failed: 0,
						executionTime,
					};
					return formatResults(results, workflowIds, executionStats);
				}

				if (!executionResult.data?.main) {
					// Handle empty results
					const results = [[]];
					const workflowIds = [targetWorkflowId];
					const executionStats = {
						total: 1,
						successful: 0,
						failed: 1,
						executionTime,
					};
					return formatResults(results, workflowIds, executionStats);
				}

				// Format successful results
				const results = executionResult.data.main;
				const workflowIds = new Array(results.length).fill(targetWorkflowId);
				const executionStats = {
					total: 1,
					successful: 1,
					failed: 0,
					executionTime,
				};

				// Clean up temporary workflow if created
				if (temporaryWorkflowId) {
					try {
						const baseUrl = this.getRestApiUrl();
						await this.helpers.httpRequest({
							method: 'DELETE',
							url: `${baseUrl}/workflows/${temporaryWorkflowId}`,
							headers: {
								'accept': 'application/json',
							},
						});
					} catch (cleanupError) {
						// Log warning but don't fail the execution
						console.warn('清理临时工作流时出现警告:', cleanupError);
					}
				}

				// For "once" mode, we need to adapt the results format
				if (outputFormat === 'default') {
					return executionResult.data.main;
				} else {
					return formatResults(results, workflowIds, executionStats);
				}
			} catch (error) {
				// Clean up temporary workflow if created (in error case)
				if (temporaryWorkflowId) {
					try {
						const baseUrl = this.getRestApiUrl();
						await this.helpers.httpRequest({
							method: 'DELETE',
							url: `${baseUrl}/workflows/${temporaryWorkflowId}`,
							headers: {
								'accept': 'application/json',
							},
						});
					} catch (cleanupError) {
						// Log warning but don't fail the execution
						console.warn('清理临时工作流时出现警告:', cleanupError);
					}
				}

				if (this.continueOnFail()) {
					const errorResult = [
						{
							json: { error: error instanceof Error ? error.message : String(error) },
							pairedItem: { item: 0 },
						},
					];

					// Format error results using the same formatting function
					if (outputFormat === 'default') {
						return [errorResult];
					} else {
						const results = [errorResult];
						const workflowIds = ['unknown'];
						const executionStats = {
							total: 1,
							successful: 0,
							failed: 1,
							executionTime: 0,
						};
						return formatResults(results, workflowIds, executionStats);
					}
				}
				throw error;
			}
		}
	}
}
